﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MessageWorkflow.Graph;
using MessageWorkflow.Handler;

namespace MessageWorkflow.Dispatch
{
    class Dispatcher
    {
        private IEnumerator<IWorkflowHandler> _workflowHandlerEnumerator;
        private IErrorStrategy _errorStrategy;
        private object _message;
        private bool _isSynchronousCall;
        private AsyncResult<Dispatcher> _asyncResult;
        private IAsyncResult _currentOperation;

        public Dispatcher(IGraph<IWorkflowHandler> workflowHandlerGraph,
            IErrorStrategy errorStrategy,
            AsyncCallback callback,
            object message)
        {
            if (workflowHandlerGraph == null)
                throw new ArgumentNullException("workflowHandlerGraph");

            if (errorStrategy == null)
                throw new ArgumentNullException("errorStrategy");

            _errorStrategy = errorStrategy;
            _message = message;
            _workflowHandlerEnumerator = workflowHandlerGraph.AsOrdered()
                                                             .GetEnumerator();

            // construct asyncResult instance to keep track of the request
            _asyncResult = new AsyncResult<Dispatcher>(callback, this);
        }

        public AsyncResult<Dispatcher> Begin()
        {
            _isSynchronousCall = true;

            MoveNext();

            return _asyncResult;
        }

        private void MoveNext()
        {
            if (_workflowHandlerEnumerator.MoveNext())
            {
                IWorkflowHandler currentHandler = _workflowHandlerEnumerator.Current;

                _currentOperation = currentHandler.BeginHandle(OnHandlerCompleted, _message);

                if (!_currentOperation.IsCompleted)
                {
                    // on this stage, we can mark synchronouscall as false since if it would be a synchronous call,
                    // OnComplete will be called before returning from the begin handle invoker
                    _isSynchronousCall = false;
                }
            }
            else
            {
                // we are done
                OnComplete();
            }
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", 
            Justification = "Exceptions are handeled by the given errorStategy")]
        private void OnHandlerCompleted(object state)
        {
            IWorkflowHandler currentHandler = _workflowHandlerEnumerator.Current;
            IAsyncResult asyncResult = (IAsyncResult)state;

            bool continueNext;
            try
            {
                currentHandler.EndHandle(asyncResult);
                continueNext = true;
            }
            catch (Exception ex)
            {
                continueNext = _errorStrategy.OnError(ex);
            }

            if (continueNext)
            {
                MoveNext();
            }
            else
            {
                OnComplete();
            }
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
            Justification = "Exceptions are handeled in the callback")]
        private void OnComplete()
        {
            Exception exception;

            try
            {
                _errorStrategy.OnComplete();
                exception = null;
            }
            catch (Exception ex)
            {
                exception = ex;
            }

            _asyncResult.SetAsCompleted(exception, _isSynchronousCall);
        }
    }
}
